package com.zijing2333.adaptivescheduling;



import android.util.Log;

import com.zijing2333.adaptivescheduling.SwitcherManager.client.ClientCallback;
import com.zijing2333.adaptivescheduling.SwitcherManager.client.SocketClient;
import com.zijing2333.adaptivescheduling.SwitcherManager.server.ServerCallback;
import com.zijing2333.adaptivescheduling.SwitcherManager.server.ServerConnectClientThread;
import com.zijing2333.adaptivescheduling.SwitcherManager.server.SocketServer;
import com.zijing2333.servicerunner.TaskInfo;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//单例模式管理类，主要负责将SocketClient与SocketServer用单例的形式创建
public final class ChannelManager { //final修饰，表示没有子类
    //打印log的标记
    private static final String TAG = "fun_ChannelManager";
    //socket端口号
    private final int SOCKET_PORT = 9527;
    //Socket服务端对象
    private SocketServer mSocketServer;
    //Socket客户端对象
    private SocketClient mSocketClient;
    //客户端Map集合对象，其中存的是任务的ID：客户端Socket对象
    private Map<String, SocketClient> mSocketClientMap = new HashMap<>();

    //服务端连接客户端线程的对象，其中存的是任务ID：ServerConnectClientThread    同时前期存的是  客户端ip：ServerConnectClientThread 后面在收到客户端发来的ip地址和任务id后，会进行一个替换
    private Map<String, ServerConnectClientThread> mServerConnectClientThreadMap = new HashMap<>();
    //线程池对象
    private ExecutorService mExecutorService;

    //可缓存线程池，先查看线程池中有没有之前创建的线程，如果有则直接使用。否则就新创建一个新的线程加入线程池中，我们经常用此类线程池来执行一些业务处理时间很短的任务。
    //这种线程池是无界线程池，即它可以创建任意条线程。
    //每次创建新线程时，先检查自己的缓存池中有没有，没有才会创建。
    //它有默认的TimeOut(=60s)，超过这个时长没活动的线程就会被自动的移除，所以完全不必要担心线程的结束问题。
    //这是比较推荐的类型。

    private static class HOLDER{ //单例的持有类，类一旦加载就创建一个单例
        private static final ChannelManager INSTANCE = new ChannelManager();
    }

    private ChannelManager(){ //无构造方法，申明出一个可缓存的线程池
        mExecutorService = Executors.newCachedThreadPool();
    }

    public static ChannelManager getInstance(){
        return HOLDER.INSTANCE;
    }

    //synchronized可以保证原子性、可见性、有序性。当它锁定一个方法或者一个代码块时，同一时刻最多只有一个线程可以执行这段代码
    //启动Socket的客户端
    public synchronized void startClient(String ipAddress, ClientCallback callback, String JsServiceId){
        if (mSocketClientMap.containsKey(JsServiceId)){ //mSocketClientMap里面主要是包含的是 服务id：服务端socket
            return;
        }
        mSocketClient = SocketClient.launch(ipAddress,SOCKET_PORT,callback);
        mSocketClientMap.put(JsServiceId,mSocketClient);
    }

    //关闭Socket的客户端
    public synchronized void stopClient(String JsServicId){
        if (mSocketClientMap.containsKey(JsServicId)){
            mSocketClientMap.get(JsServicId).closeConnect();
            mSocketClientMap.remove(JsServicId);
            mSocketClient.closeConnect();
            mSocketClient = null;
        }
    }

    //启动Socket服务端
    public void startServer(ServerCallback callback){
        if (mSocketServer != null){ //如果说第一次启动的话，正常启动；如果是再次启动的话，他会将原先启动的ServerSocket与ClientSocket全部置空，然后在重新创建
            closeChannel();
        }
        mSocketServer = SocketServer.launch(SOCKET_PORT,callback);
    }

    public void stopServer(){
        if (mSocketServer != null){
            mSocketServer.stopServer();
            mSocketServer = null;
        }
    }


    public void addServerConnectClientThreadMap(String clientIp,ServerConnectClientThread serverConnectClientThread){
        mServerConnectClientThreadMap.put(clientIp,serverConnectClientThread);
    }
    public void removeServerConnectClientThreadFromMap(String taskId){
        if (mServerConnectClientThreadMap.containsKey(taskId)){
            mServerConnectClientThreadMap.get(taskId).close(); //对ServerConnectClientThread资源进行关闭
            mServerConnectClientThreadMap.remove(taskId);
        }
    }
    public void exchangeIPwithTaskId(String clientIp,String JstaskId){
        ServerConnectClientThread serverConnectClientThread = mServerConnectClientThreadMap.remove(clientIp);
        mServerConnectClientThreadMap.put(JstaskId,serverConnectClientThread);
    }
    public ServerConnectClientThread getServerConnectClientThreadFromMap(String TaskId){
        return mServerConnectClientThreadMap.get(TaskId);
    }



    //发送信息到客户端当中
    public void sendMesToClient(String JsServiceId ,String type,String msg){
        mExecutorService.submit(()->{
            mSocketServer.writeString(JsServiceId,type,msg); //原版聊天是调用write方法，再把ClienConnectServer中的run方法换掉即可
        });
    }


    public void senMesToServer(String JsServiceName, String type,String msg) {
        mExecutorService.submit(()->{
            mSocketClientMap.get(JsServiceName).writeString(JsServiceName,type,msg);
//            mSocketClient.writeString(JsServiceName, msg);
        });
    }




    /**
     * 关闭客户端以及服务端管道，SocketServer以及SocketClient
     */
    public void closeChannel() {
        if (mSocketServer != null){
            mSocketServer.stopServer();
            mSocketServer = null;
        }

        Set<String> lists = mSocketClientMap.keySet();//取出Map集合的所有Key
        for (String str:lists){
            if (mSocketClientMap.get(str)!=null){
                mSocketClientMap.get(str).closeConnect();
                mSocketClientMap.remove(str);
            }
        }
    }

}
